package com.alogic.cert.xscript;

import com.alogic.cert.CertificateContent;
import com.alogic.cert.CertificateStore;
import com.alogic.cert.CertificateStoreFactory;
import com.alogic.cert.PemCertificateContent;
import com.alogic.xscript.ExecuteWatcher;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.Script;
import com.alogic.xscript.doc.XsObject;
import com.anysoft.util.*;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.math.BigInteger;
import java.util.Stack;

/**
 * NewCert
 *
 * @since 1.6.13.40 [20210306 duanyy] <br>
 */
public class NewCert extends NS{
    protected String cid = "$cert";
    protected String $sn;
    protected String $x500Name;
    protected boolean isRoot = false;

    protected Logiclet onBuild = null;

    public NewCert(String tag, Logiclet p) {
        super(tag, p);
    }

    @Override
    public void configure(Properties p){
        super.configure(p);

        $sn = PropertiesConstants.getRaw(p,"sn","");
        $x500Name = PropertiesConstants.getRaw(p,"x500Name",$x500Name);
        isRoot = PropertiesConstants.getBoolean(p,"root",isRoot,true);
        cid = PropertiesConstants.getString(p, "cid", cid,true);
    }

    @Override
    public void configure(Element element, Properties props) {
        XmlElementProperties p = new XmlElementProperties(element, props);

        Element result = XmlTools.getFirstElementByPath(element,"on-result");

        if (result != null) {
            NodeList nodeList = result.getChildNodes();

            for (int i = 0; i < nodeList.getLength(); i++) {
                Node n = nodeList.item(i);

                if (n.getNodeType() != Node.ELEMENT_NODE) {
                    //只处理Element节点
                    continue;
                }

                Element e = (Element) n;
                String xmlTag = e.getNodeName();
                Logiclet statement = createLogiclet(xmlTag, this);

                if (statement != null) {
                    statement.configure(e, p);
                    if (statement.isExecutable()) {
                        children.add(statement);
                    }
                } else {
                    log(String.format("Plugin %s is not found,ignored.", xmlTag), "warning");
                }
            }
        }

        Element build = XmlTools.getFirstElementByPath(element,"on-build");
        if (build != null){
            onBuild = Script.create(build, props);
        }

        configure(p);
    }

    @Override
    protected void onExecute(XsObject root, XsObject current, LogicletContext ctx, ExecuteWatcher watcher) {
        if (isRoot){
            CertificateStore store = CertificateStoreFactory.getDefault();
            long sn = PropertiesConstants.transform(ctx, $sn, 0);
            if (sn <= 0) {
                sn = System.currentTimeMillis() * 10000 + Integer.parseInt(KeyGen.uuid(5, 0, 9));
            }

            CertificateContent newPem = store.newCertificate(
                    BigInteger.valueOf(sn),
                    new PemCertificateContent(),
                    null,
                    onBuild,
                    PropertiesConstants.transform(ctx, $x500Name, ""),
                    ctx);

            Stack<CertificateContent> stack = ctx.getObject(cid);
            if (stack == null){
                stack = new Stack<CertificateContent>();
                try {
                    ctx.setObject(cid, stack);
                    stack.add(newPem);
                    super.onExecute(root, current, ctx, watcher);
                } finally {
                    stack.pop();
                    ctx.removeObject(cid);
                }
            }else{
                try {
                    stack.add(newPem);
                    super.onExecute(root, current, ctx, watcher);
                } finally {
                    stack.pop();
                }
            }
        }else {
            CertificateStore store = CertificateStoreFactory.getDefault();
            Stack<CertificateContent> stack = ctx.getObject(cid);
            if (stack == null) {
                stack = new Stack<CertificateContent>();
                CertificateContent pem = store.getRoot(new PemCertificateContent());
                stack.add(pem);

                CertificateContent issuer = stack.peek();
                long sn = PropertiesConstants.transform(ctx, $sn, 0);
                if (sn <= 0) {
                    sn = System.currentTimeMillis() * 10000 + Integer.parseInt(KeyGen.uuid(5, 0, 9));
                }

                CertificateContent newPem = store.newCertificate(
                        BigInteger.valueOf(sn),
                        new PemCertificateContent(),
                        issuer,
                        onBuild,
                        PropertiesConstants.transform(ctx, $x500Name, ""),
                        ctx);

                try {
                    ctx.setObject(cid, stack);
                    stack.add(newPem);
                    super.onExecute(root, current, ctx, watcher);
                } finally {
                    stack.pop();
                    ctx.removeObject(cid);
                }
            } else {
                CertificateContent issuer = stack.peek();
                long sn = PropertiesConstants.transform(ctx, $sn, 0);
                if (sn <= 0) {
                    sn = System.currentTimeMillis() * 1000000L + System.nanoTime() % 1000000L;
                }

                CertificateContent newPem = store.newCertificate(
                        BigInteger.valueOf(sn),
                        new PemCertificateContent(),
                        issuer,
                        onBuild,
                        PropertiesConstants.transform(ctx, $x500Name, ""),
                        ctx);

                try {
                    stack.add(newPem);
                    super.onExecute(root, current, ctx, watcher);
                } finally {
                    stack.pop();
                }
            }
        }
    }
}